<?php

class Crud_Form_Element_Many2many extends Crud_Form_Element
{
	/**
	 * Primary table for creating element.
	 * 
	 * @var Crud_Db_Table
	 */
	protected $_table;
	
	/**
	 * Dependent many2many table.
	 * 
	 * @var Crud_Section 
	 */
	protected $_m2mTable;
	
	public function __construct($table, $m2mTable)
	{
		if (!$table instanceof Crud_Section) {
			$message = 'Cant create many2many element. Primary table is invalid Crud_Section';
			throw new Crud_Exception($message);
		}
		
		if (!$m2mTable instanceof Crud_Section) {
			$message = 'Cant create many2many element. Dependent table is invalid Crud_Section';
			throw new Crud_Exception($message);
		}

		$this->_table = $table;
		$this->_m2mTable = $m2mTable;
		$this->init();
	}
	
	/**
	 * Return primary table for creating element.
	 * 
	 * @return Crud_Section
	 */
	public function getTable()
	{
		return $this->_table;
	}
	
	/**
	 * Dependent many2many table.
	 * 
	 * @return Crud_Section 
	 */
	public function getM2mTable()
	{
		return $this->_m2mTable;
	}
	
	/**
	 * Create form element.
	 * 
	 * @return void
	 */
	public function init()
	{
		// Set element name from m2m table
		$name = $this->getM2mTable()->getTableName();
		$class = $this->_getElementClass();
		$element = new $class($name);
		
		$this->setElement($element);
		
		$this->_initMultiOptions();
		$this->_initDecorators();
	}
	
	/**
	 * Return element class.
	 * 
	 * @return string
	 */
	protected function _getElementClass()
	{
		return 'Zend_Form_Element_MultiCheckbox';
	}
	
	/**
	 * Setup default values for element.
	 * 
	 * @param string $filter Suggest filter for getFKValues.
	 * @return void
	 */
	protected function _initMultiOptions($filter = null)
	{
		$table = $this->getTable();
		$m2mTable = $this->getM2mTable();
		$column3rd = $m2mTable->getFKColumnNotFor($table);
		
		// Set options
		$values = $column3rd->getFKValues(null, $filter);
		$this->getElement()->setMultiOptions($values);
	}
	
	/**
	 * Setup decorators for element.
	 * 
	 * @return void
	 */
	protected function _initDecorators()
	{
		$this->_addMultiColumnDecorator();
	}
	
	/**
	 * Adding multi-column decorator.
	 * 
	 * @return void
	 */
	protected function _addMultiColumnDecorator()
	{
		$this->_addDecoratorAfterViewHelper('MultiColumn');
	}
	
	/**
	 * Adding decorator after ViewHelper in list.
	 * 
	 * @param string|Zend_Form_Decorator_Abstract $d
	 * @return void
	 */
	protected function _addDecoratorAfterViewHelper($d)
	{
		// Add decorator after view helper
		$element = $this->getElement();
		
		$decorators = $element->getDecorators();
		$element->clearDecorators();
		foreach ($decorators as $key => $decorator) {
			$element->addDecorator($decorator);
			if (strstr($key, 'ViewHelper')) {
				$element->addDecorator($d);
			}
		}
	}
	
	/**
	 * Set value for given row.
	 * 
	 * @param Crud_Section_Row
	 * @param mixed $value
	 * @return void
	 */
	public function setRowValue(Crud_Section_Row $row, $value)
	{
		$table = $this->getTable();
		$m2mTable = $this->getM2mTable();
		
		$column = $m2mTable->getFKColumnFor($table);
		$column3rd = $m2mTable->getFKColumnNotFor($table);
		
		$select = $m2mTable->getPeer()->select();
		$select->where($column->getKey() . ' = ?', $row->getId());
		if ($value) {
			$select->where($column3rd->getKey() .  Db_Select::SQL_NOT_IN, $value);
		}
		
		$forDeleteRowset = $m2mTable->getPeer()->fetchAll($select);
		foreach ($forDeleteRowset as $m2mRow) {
			$m2mRow->delete();
		}
		
		if (!$value) return;
		
		// Next, find all existing rows
		$exist = array();
		if ($value) {
			$select = $m2mTable->getPeer()->select();
			$select->where($column3rd->getKey() .  Db_Select::SQL_IN, $value);
			$select->where("{$column->getKey()} = ?", $row->getId());
			$existingRowset = $m2mTable->getPeer()->fetchAll($select);
			
			$getter = $column3rd->getGetter();
			foreach ($existingRowset as $existRow) {
				$exist[ $existRow->$getter() ] = $existRow->$getter();
			}
		}
		
		$setter = $column->getSetter();
		$setter3rd = $column3rd->getSetter();
		foreach ($value as $val) {
			if (isset($exist[$val])) continue;
			$nRow = $m2mTable->getPeer()->createRow();
			$nRow->$setter($row->getId());
			$nRow->$setter3rd($val);
			$nRow->save();
		}
	}
	
	/**
	 * Get default value by row.
	 * 
	 * @param Crud_Section_Row
	 * @return void
	 */
	public function getRowValue(Crud_Section_Row $row = null)
	{
		if (!$row) {
			return null;
		}
		
		$section = $this->getTable();
		$m2m = $this->getM2mTable();
		
		$column = $m2m->getFKColumnFor($section);
		$column3rd = $m2m->getFKColumnNotFor($section);
		
		$peer = $m2m->getPeer();
		$select = $peer->select();
		$select->where("`{$column->getKey()}` = ?", $row->getId());
		$rowset = $peer->fetchAll($select);
		
		$values = array();
		foreach ($rowset as $m2mRow) {
			$method = $column3rd->getGetter();
			$values[] = $m2mRow->$method($column3rd->getKey());
		}
		
		return $values;
	}
	
}